using System.Text;

namespace WeatherSystem.API.Middleware
{
    /// <summary>
    /// HTTP请求日志中间件
    /// 记录HTTP请求的参数和返回数据
    /// </summary>
    public class HttpLoggingMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger<HttpLoggingMiddleware> _logger;

        public HttpLoggingMiddleware(RequestDelegate next, ILogger<HttpLoggingMiddleware> logger)
        {
            _next = next;
            _logger = logger;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            // 记录请求开始时间
            var startTime = DateTime.UtcNow;
            var requestId = Guid.NewGuid().ToString("N")[..8];

            // 读取请求体
            var requestBody = await ReadRequestBodyAsync(context.Request);

            // 记录请求信息
            LogRequest(context.Request, requestBody, requestId);

            // 保存原始响应流
            var originalResponseBodyStream = context.Response.Body;

            try
            {
                // 创建内存流来捕获响应
                using var responseBodyStream = new MemoryStream();
                context.Response.Body = responseBodyStream;

                // 执行下一个中间件
                await _next(context);

                // 读取响应体
                var responseBody = await ReadResponseBodyAsync(responseBodyStream);

                // 记录响应信息
                var duration = DateTime.UtcNow - startTime;
                LogResponse(context.Response, responseBody, requestId, duration);

                // 将响应写回原始流
                await responseBodyStream.CopyToAsync(originalResponseBodyStream);
            }
            finally
            {
                // 恢复原始响应流
                context.Response.Body = originalResponseBodyStream;
            }
        }

        /// <summary>
        /// 读取请求体
        /// </summary>
        private async Task<string> ReadRequestBodyAsync(HttpRequest request)
        {
            try
            {
                // 启用缓冲以允许多次读取
                request.EnableBuffering();

                // 读取请求体
                using var reader = new StreamReader(request.Body, Encoding.UTF8, leaveOpen: true);
                var body = await reader.ReadToEndAsync();

                // 重置流位置
                request.Body.Position = 0;

                return body;
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "读取请求体时发生错误");
                return "[无法读取请求体]";
            }
        }

        /// <summary>
        /// 读取响应体
        /// </summary>
        private async Task<string> ReadResponseBodyAsync(MemoryStream responseBodyStream)
        {
            try
            {
                responseBodyStream.Seek(0, SeekOrigin.Begin);
                using var reader = new StreamReader(responseBodyStream, Encoding.UTF8, leaveOpen: true);
                var body = await reader.ReadToEndAsync();
                responseBodyStream.Seek(0, SeekOrigin.Begin);
                return body;
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "读取响应体时发生错误");
                return "[无法读取响应体]";
            }
        }

        /// <summary>
        /// 记录请求信息
        /// </summary>
        private void LogRequest(HttpRequest request, string requestBody, string requestId)
        {
            try
            {
                var logData = new
                {
                    RequestId = requestId,
                    Method = request.Method,
                    Path = request.Path.Value,
                    QueryString = request.QueryString.Value,
                    Headers = request.Headers.ToDictionary(h => h.Key, h => string.Join(", ", h.Value.ToArray())),
                    Body = ShouldLogBody(request) ? TruncateIfNeeded(requestBody) : "[跳过记录]",
                    ContentType = request.ContentType,
                    UserAgent = request.Headers.UserAgent.ToString(),
                    RemoteIpAddress = request.HttpContext.Connection.RemoteIpAddress?.ToString(),
                    Timestamp = DateTime.UtcNow
                };

                _logger.LogInformation("HTTP请求 [{RequestId}]: {Method} {Path}{QueryString} | 请求体: {Body} | 来源IP: {RemoteIpAddress}",
                    requestId, request.Method, request.Path, request.QueryString, 
                    logData.Body, logData.RemoteIpAddress);
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "记录请求信息时发生错误 [{RequestId}]", requestId);
            }
        }

        /// <summary>
        /// 记录响应信息
        /// </summary>
        private void LogResponse(HttpResponse response, string responseBody, string requestId, TimeSpan duration)
        {
            try
            {
                var logData = new
                {
                    RequestId = requestId,
                    StatusCode = response.StatusCode,
                    Headers = response.Headers.ToDictionary(h => h.Key, h => string.Join(", ", h.Value.ToArray())),
                    Body = ShouldLogResponseBody(response) ? TruncateIfNeeded(responseBody) : "[跳过记录]",
                    ContentType = response.ContentType,
                    Duration = duration.TotalMilliseconds,
                    Timestamp = DateTime.UtcNow
                };

                var logLevel = response.StatusCode >= 400 ? LogLevel.Warning : LogLevel.Information;
                _logger.Log(logLevel, "HTTP响应 [{RequestId}]: {StatusCode} | 耗时: {Duration}ms | 响应体: {Body}",
                    requestId, response.StatusCode, duration.TotalMilliseconds, logData.Body);
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "记录响应信息时发生错误 [{RequestId}]", requestId);
            }
        }

        /// <summary>
        /// 判断是否应该记录请求体
        /// </summary>
        private bool ShouldLogBody(HttpRequest request)
        {
            // 跳过文件上传和大型请求
            if (request.ContentLength > 10 * 1024) // 10KB
                return false;

            // 只记录JSON和表单数据
            var contentType = request.ContentType?.ToLower();
            return contentType != null && (
                contentType.Contains("application/json") ||
                contentType.Contains("application/x-www-form-urlencoded") ||
                contentType.Contains("text/plain")
            );
        }

        /// <summary>
        /// 判断是否应该记录响应体
        /// </summary>
        private bool ShouldLogResponseBody(HttpResponse response)
        {
            // 跳过大型响应
            if (response.ContentLength > 10 * 1024) // 10KB
                return false;

            // 只记录JSON和文本响应
            var contentType = response.ContentType?.ToLower();
            return contentType != null && (
                contentType.Contains("application/json") ||
                contentType.Contains("text/plain") ||
                contentType.Contains("text/html")
            );
        }

        /// <summary>
        /// 截断过长的内容
        /// </summary>
        private string TruncateIfNeeded(string content, int maxLength = 2000)
        {
            if (string.IsNullOrEmpty(content))
                return content;

            return content.Length > maxLength 
                ? content[..maxLength] + "...[已截断]"
                : content;
        }
    }

    /// <summary>
    /// HTTP日志中间件扩展方法
    /// </summary>
    public static class HttpLoggingMiddlewareExtensions
    {
        public static IApplicationBuilder UseCustomHttpLogging(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<HttpLoggingMiddleware>();
        }
    }
}